16.4. 函数
Java 和 C 之间的主要区别之一是 C 是一种命令式和过程式语言,而 Java 是一种面向对象语言。在 C 中,程序被组织为一个或多个函数。每个 C 程序都必须至少有一个“main”函数,但通常还有许多其他函数。在 Java 中,程序被组织为一组相互作用的对象。类定义定义对象的状态和方法以及与类关联的静态定义和方法。Java 中没有类定义之外的函数。由单个类组成的 Java 程序没有数据成员,只有“public static”方法函数,这种程序在设计上与 C 程序最为相似。
函数将代码分解为可管理的部分并减少代码重复。函数可能需要零个或多个参数作为输入,并且返回特定类型的单个值。函数声明或原型指定了函数的名称、返回类型以及参数列表(所有参数的数量和类型)。函数定义包括调用函数时要执行的代码。C 中的所有函数都必须在调用之前声明。这可以通过声明函数原型或在调用函数之前完整定义该函数来实现:
// function definition format:
// ---------------------------
<return type> <function name> (<parameter list>)
{
<function body>
}
// parameter list format:
// ---------------------
<type> <param1 name>, <type> <param2 name>, ..., <type> <last param name>
以下是一个示例函数定义。请注意,注释描述了函数的功能、每个参数的详细信息(其用途和应传递的内容)以及函数返回的内容:
/* This program computes the larger of two
* values entered by the user.
*/
#include <stdio.h>
/* max: computes the larger of two integer values
* x: one integer value
* y: the other integer value
* returns: the larger of x and y
*/
int max(int x, int y) {
int bigger;
bigger = x;
if (y > x) {
bigger = y;
}
printf(" in max, before return x: %d y: %d\n", x, y);
return bigger;
}
没有返回值的函数应该指定 void
返回类型。以下是 void
函数的示例:
/* prints out the squares from start to stop
* start: the beginning of the range
* stop: the end of the range
*/
void print_table(int start, int stop) {
int i;
for (i = start; i <= stop; i++) {
printf("%d\t", i*i);
}
printf("\n");
}
与任何支持函数或过程的编程语言一样,函数调用 会调用一个函数,并为特定调用传递特定的参数值。函数通过其名称调用并传递参数,每个对应的函数参数都有一个参数。在 C 语言中,调用函数的方式如下:
// function call format:
// ---------------------
function_name(<argument list>);
// argument list format:
// ---------------------
<argument 1 expression>, <argument 2 expression>, ..., <last argument expression>
C 函数的参数是按值传递(passed by value)的:每个函数参数都被赋予调用者在函数调用中传递给它的相应参数的 值。按值传递语义意味着函数中对参数值的任何更改(即,在函数中为参数分配新值)对调用者来说都是 不可见的。
以下是对前面列出的 max
和 print_table
函数的一些示例函数调用:
int val1, val2, result;
val1 = 6;
val2 = 10;
/* to call max, pass in two int values, and because max returns an
int value, assign its return value to a local variable (result)
*/
result = max(val1, val2); /* call max with argument values 6 and 10 */
printf("%d\n", result); /* prints out 10 */
result = max(11, 3); /* call max with argument values 11 and 3 */
printf("%d\n", result); /* prints out 11 */
result = max(val1 * 2, val2); /* call max with argument values 12 and 10 */
printf("%d\n", result); /* prints out 12 */
/* print_table does not return a value, but takes two arguments */
print_table(1, 20); /* prints a table of values from 1 to 20 */
print_table(val1, val2); /* prints a table of values from 6 to 10 */
下面是完整程序的另一个示例,它展示了对max
函数略有不同的实现的调用,该函数有一个附加语句来更改其参数的值(x = y
):
/* max: computes the larger of two int values
* x: one value
* y: the other value
* returns: the larger of x and y
*/
int max(int x, int y) {
int bigger;
bigger = x;
if (y > x) {
bigger = y;
// note: changing the parameter x's value here will not
// change the value of its corresponding argument
x = y;
}
printf(" in max, before return x: %d y: %d\n", x, y);
return bigger;
}
/* main: shows a call to max */
int main(void) {
int a, b, res;
printf("Enter two integer values: ");
scanf("%d%d", &a, &b);
res = max(a, b);
printf("The larger value of %d and %d is %d\n", a, b, res);
return 0;
}
以下输出显示了该程序两次运行的结果。请注意两次运行中参数x
的值(从max
函数内部打印)的差异。具体来说,请注意在第二次运行中更改参数x
的值不会影响调用返回后作为参数传递给max
的变量:
$ ./a.out
Enter two integer values: 11 7
in max, before return x: 11 y: 7
The larger value of 11 and 7 is 11
$ ./a.out
Enter two integer values: 13 100
in max, before return x: 100 y: 100
The larger value of 13 and 100 is 100
因为参数是通过 值传递 给函数的,所以改变其某个参数值的max
函数的先前版本的行为与不改变其参数值的max
的原始版本的行为完全相同。
16.4.1. 运行栈帧
执行栈 跟踪程序中活动函数的状态。每个函数调用都会创建一个新的栈帧(有时称为激活帧或激活记录),其中包含其参数和局部变量值。栈顶的帧是活动帧;它表示当前正在执行的函数激活,并且只有其局部变量和参数在范围内。调用一个函数时,会为其创建一个新的栈帧( 推送 到栈顶),并在新帧中为其局部变量和参数分配空间。函数返回时,其栈帧将从栈中移除(从栈顶弹出),而调用者的栈帧则留在栈顶。
对于前面的示例程序,在 max
执行 return
语句之前的执行点,执行堆栈将类似于图 1。回想一下,main
传递给 max
的参数值是通过值传递的,这意味着 max
的参数 x
和 y
被赋予了它们对应参数 a
和 b
的值,这些值来自main
中的调用。尽管 max
函数改变了 x
的值,但这种变化不会影响 main
中 a
的值。
图 1. 从 max 函数返回之前的执行堆栈内容
以下完整程序包含两个函数,并展示了从 main
函数调用它们的示例。在这个程序中,我们在 main
函数上方声明了 max
和 print_table
的函数原型,以便 main
尽管先定义,也可以访问它们。main
函数包含完整程序的高级步骤,首先定义它呼应了程序自上而下的设计。此示例包含注释,描述了程序中对函数和函数调用很重要的部分。您还可以下载并运行 完整程序。
/* This file shows examples of defining and calling C functions.
* It also demonstrates using scanf().
*/
#include <stdio.h>
/* This is an example of a FUNCTION PROTOTYPE. It declares just the type
* information for a function (the function's name, return type, and parameter
* list). A prototype is used when code in main wants to call the function
* before its full definition appears in the file.
*/
int max(int n1, int n2);
/* A prototype for another function. void is the return type of a function
* that does not return a value
*/
void print_table(int start, int stop);
/* All C programs must have a main function. This function defines what the
* program does when it begins executing, and it's typically used to organize
* the big-picture behavior of the program.
*/
int main(void) {
int x, y, larger;
printf("This program will operate over two int values.\n");
printf("Enter the first value: ");
scanf("%d", &x);
printf("Enter the second value: ");
scanf("%d", &y);
larger = max(x, y);
printf("The larger of %d and %d is %d\n", x, y, larger);
print_table(x, larger);
return 0;
}
/* This is an example of a FUNCTION DEFINITION. It specifies not only the
* function name and type, but it also fully defines the code of its body.
* (Notice, and emulate, the complete function comment!)
*/
/* Computes the max of two integer values.
* n1: the first value
* n2: the other value
* returns: the larger of n1 and n2
*/
int max(int n1, int n2) {
int result;
result = n1;
if (n2 > n1) {
result = n2;
}
return result;
}
/* prints out the squares from start to stop
* start: the beginning of the range
* stop: the end of the range
*/
void print_table(int start, int stop) {
int i;
for (i = start; i <= stop; i++) {
printf("%d\t", i*i);
}
printf("\n");
}